package com.ruoyi.qywx.service;

import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.ruoyi.common.constant.ErrorConstants;
import com.ruoyi.common.core.cache.Meta;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.function.InsertBatchFunction;
import com.ruoyi.common.utils.CollectionUtils;
import com.ruoyi.common.utils.NumberUtils;
import com.ruoyi.common.utils.StrUtils;
import com.ruoyi.qywx.entity.ContactTag;
import com.ruoyi.qywx.entity.ContactTagGroup;
import com.ruoyi.qywx.entity.ExtUserTag;
import com.ruoyi.qywx.enums.TagEnum;
import com.ruoyi.qywx.getway.api.ContactTagApi;
import com.ruoyi.qywx.getway.api.SyncAppApi;
import com.ruoyi.qywx.getway.entity.QyExternalUserTag;
import com.ruoyi.qywx.getway.entity.QyTagGroup;
import com.ruoyi.qywx.mapper.ContactTagGroupMapper;
import com.ruoyi.qywx.mapper.ContactTagMapper;
import com.ruoyi.qywx.mapper.ExtUserTagMapper;
import com.ruoyi.qywx.model.vo.ContactTagGroupVo;
import com.ruoyi.qywx.model.vo.TagVo;
import com.ruoyi.qywx.service.inters.ContactTagServic;
import com.ruoyi.system.service.ISysTeamService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.ruoyi.common.core.domain.AjaxResult.error;
import static com.ruoyi.common.core.domain.AjaxResult.success;

/**
 * @author : hcl
 * @Date : 2023/8/11 14:37
 */
@Service
@Slf4j
public class ContactTagServicImpl implements ContactTagServic {
    @Resource
    private ContactTagMapper tagMapper;
    @Resource
    private ContactTagGroupMapper tagGroupMapper;
    @Autowired
    private ISysTeamService iSysTeamService;
    @Resource
    private ExtUserTagMapper extUserTagMapper;


    @Override
    public List<ContactTagGroupVo> tagList(Integer type, Long teamId) {
        List<ContactTagGroup> tagGroups = tagGroupMapper.selectList(new QueryWrapper<ContactTagGroup>()
                .eq("type", type)
                .eq("team_id", teamId));
        if (CollectionUtils.isEmpty(tagGroups)) {
            return Collections.emptyList();
        }
        List<ContactTagGroupVo> groupVos = tagGroups.stream().map(g -> new ContactTagGroupVo(g.getId(), g.getGroupName()))
                .collect(Collectors.toList());
        List<ContactTag> tags = tagMapper.selectList(new QueryWrapper<ContactTag>()
                .eq("type", type)
                .eq("team_id", teamId));

        if (CollectionUtils.isNotEmpty(tags)) {
            Map<Long, List<TagVo>> tagMap = tags.stream()
                    .collect(Collectors.groupingBy(ContactTag::getGroupId,
                            Collectors.mapping(tag -> new TagVo(tag.getId(),
                                    tag.getName()), Collectors.toList())));
            for (ContactTagGroupVo group : groupVos) {
                group.setTags(tagMap.getOrDefault(group.getId(), Collections.emptyList()));
            }
        }
        return groupVos;
    }

    /**
     * @param tagIds tagIds
     * @return 返回 id : 名称
     */
    @Override
    public Map<Long, String> tagsTurnNameMap(Collection<Long> tagIds, Long teamId) {
        List<ContactTag> contactTags = null;
        if (CollectionUtils.isNotEmpty(tagIds)) {
            contactTags = tagMapper.selectList(new QueryWrapper<ContactTag>()
                    .eq("team_id", teamId)
                    .in("id", tagIds));
        }
        if (CollectionUtils.isNotEmpty(contactTags)) {
            return contactTags.stream().collect(Collectors.toMap(ContactTag::getId, ContactTag::getName));
        }
        return Collections.emptyMap();
    }

    @Transactional
    @Override
    public AjaxResult createGroup(ContactTagGroup group) {
        if (NumberUtils.isTrue(group.getId())) {
            ContactTagGroup old = tagGroupMapper.selectById(group.getId());
            if (old == null || !old.getTeamId().equals(group.getTeamId())) {
                return error(ErrorConstants.NO_DATA);
            }
            List<ContactTagGroup> repeats = tagGroupMapper.selectList(
                    new QueryWrapper<ContactTagGroup>()
                            .eq("team_id", group.getTeamId())
                            .eq("group_name", group.getGroupName())
                            .ne("id", group.getId()));
            if (CollectionUtils.isNotEmpty(repeats)) {
                return error("名称不能重复");
            }
            if (group.getType() == TagEnum.QYWX.ordinal()) {
                ContactTagApi.updateTagOrGroupName(iSysTeamService.selectTeamMeta(group.getTeamId()),
                        old.getWxId(),
                        group.getGroupName());
            }
            updateTagGroupById(group);
        } else {
            QueryWrapper<ContactTagGroup> wrapper = new QueryWrapper<>();
            wrapper.eq("team_id", group.getTeamId())
                    .eq("group_name", group.getGroupName());
            ContactTagGroup old = tagGroupMapper.selectOne(wrapper);
            if (old != null) {
                return error("名称不能重复");
            }
            tagGroupMapper.insert(group);
        }
        return AjaxResult.success();
    }

    @Transactional
    @Override
    public void updateTagGroupById(ContactTagGroup group) {
        tagGroupMapper.updateById(group);
    }

    @Transactional
    @Override
    public void updateTagName(ContactTag contactTag) {
        tagMapper.updateById(contactTag);
    }

    @Transactional
    @Override
    public void insertTagGroup(ContactTagGroup group) {
        tagGroupMapper.insert(group);
    }

    @Transactional
    @Override
    public AjaxResult createTag(ContactTag contactTag) {
        QueryWrapper<ContactTag> wrapper = new QueryWrapper<>();
        ContactTagGroup group = tagGroupMapper.selectById(contactTag.getGroupId());
        if (Objects.isNull(group)) {
            return error("请选择分组");
        }
        if (NumberUtils.isTrue(contactTag.getId())) {
            wrapper.eq("group_id", contactTag.getGroupId())
                    .eq("id", contactTag.getId());
            ContactTag oldTag = tagMapper.selectOne(wrapper);
            if (oldTag == null || !oldTag.getTeamId().equals(contactTag.getTeamId())) {
                return error(ErrorConstants.NO_DATA);
            }
            Long repeats = tagMapper.selectCount(
                    new QueryWrapper<ContactTag>()
                            .eq("team_id", contactTag.getTeamId())
                            .eq("name", contactTag.getName())
                            .eq("group_id", contactTag.getGroupId())
                            .ne("id", contactTag.getId()));
            if (NumberUtils.isTrue(repeats)) {
                return error("名称不能重复");
            }
            if (contactTag.getType() == TagEnum.QYWX.ordinal()) {
                ContactTagApi.updateTagOrGroupName(iSysTeamService.selectTeamMeta(contactTag.getTeamId()),
                        oldTag.getWxId(),
                        contactTag.getName());
            }
            tagMapper.updateById(contactTag);
        } else {
            wrapper.eq("name", contactTag.getName())
                    .eq("group_id", contactTag.getGroupId());
            ContactTag exit = tagMapper.selectOne(wrapper);
            if (exit != null) {
                return error("标签名称不能重复");
            }
            if (contactTag.getType() == TagEnum.QYWX.ordinal()) {
                JSONObject json = ContactTagApi.insertTag(iSysTeamService.selectTeamMeta(contactTag.getTeamId()),
                        contactTag.getName(),
                        group.getWxId(),
                        group.getGroupName());
                JSONObject wxTagGroup = json.getJSONObject("tag_group");
                if (StrUtils.isEmpty(group.getWxId())) {
                    tagGroupMapper.updateById(ContactTagGroup.builder()
                            .id(group.getId())
                            .wxId(wxTagGroup.getStr("group_id"))
                            .build());
                }
                JSONObject tag = (JSONObject) wxTagGroup.getJSONArray("tag").get(0);
                contactTag.setWxId(tag.getStr("id"));
            }
            tagMapper.insert(contactTag);
        }
        return AjaxResult.success(Collections.singletonMap("id", contactTag.getId()));
    }

    @Transactional
    @Override
    public void insertTag(List<ContactTag> contactTag) {
        if (CollectionUtils.isNotEmpty(contactTag)) {
            tagMapper.insertBatchSomeColumn(contactTag);
        }
    }

    @Override
    public List<ContactTagGroup> selectQiyeGroups(Meta meta, List<String> groupWxId) {
        if (Objects.isNull(meta) || !NumberUtils.isTrue(meta.getTeamId())) {
            return Collections.emptyList();
        }
        QueryWrapper<ContactTagGroup> wrapper = new QueryWrapper<>();
        if (CollectionUtils.isNotEmpty(groupWxId)) {
            wrapper.in("wx_id", groupWxId);
        } else {
            wrapper.eq("type", TagEnum.QYWX.ordinal());
        }
        wrapper.eq("team_id", meta.getTeamId());

        return tagGroupMapper.selectList(wrapper);
    }

    @Transactional
    @Override
    public void deleteTagGroups(Collection<Long> groupIds) {
        if (CollectionUtils.isNotEmpty(groupIds)) {
            tagGroupMapper.deleteBatchIds(groupIds);
        }
    }

    @Transactional
    @Override
    public AjaxResult deleteTagGroup(Integer type, Long id, Long teamId) {
        ContactTagGroup group = selectGroupById(id);
        if (Objects.isNull(group) || !group.getTeamId().equals(teamId)) {
            return error(ErrorConstants.NO_DATA);
        }
        Long tagCount = tagOfGroupCount(id);
        if (NumberUtils.isTrue(tagCount)) {
            return error("该分组下仍存在标签，不能删除");
        }
        tagGroupMapper.deleteById(id);
        return success();
    }

    @Transactional
    @Override
    public AjaxResult deleteTag(Integer type, Long id, Long teamId) {
        ContactTag tag = tagMapper.selectById(id);
        if (Objects.isNull(tag) || !tag.getTeamId().equals(teamId)) {
            return error(ErrorConstants.NO_DATA);
        }
        if (type == TagEnum.QYWX.ordinal()) {
            ContactTagApi.deleteTag(tag.getWxId(), iSysTeamService.selectTeamMeta(teamId));
        }
        tagMapper.deleteById(id);
        if (type == TagEnum.QYWX.ordinal()) {
            Long tagCount = tagOfGroupCount(tag.getGroupId());
            if (tagCount == 0) {
                tagGroupMapper.deleteById(tag.getGroupId());
            }
        }
        return success();
    }

    @Transactional
    @Override
    public void deleteQywxNoexistGroups(List<Long> groupIds) {
        if (CollectionUtils.isNotEmpty(groupIds)) {
            tagMapper.delete(new QueryWrapper<ContactTag>().in("group_id", groupIds));
            deleteTagGroups(groupIds);
        }
    }

    @Transactional
    @Override
    public void deleteTags(List<Long> tagIds) {
        if (CollectionUtils.isNotEmpty(tagIds)) {
            tagMapper.deleteBatchIds(tagIds);
        }
    }

    @Override
    public List<ContactTag> selectTagsByGroupId(Long groupId) {
        if (NumberUtils.isTrue(groupId)) {
            return tagMapper.selectList(new QueryWrapper<ContactTag>().eq("group_id", groupId));
        }
        return Collections.emptyList();
    }

    @Override
    public ContactTagGroup selectGroupById(Long id) {
        return tagGroupMapper.selectById(id);
    }

    @Override
    public List<ContactTag> selectTagByIds(List<Long> tagIds) {
        if (CollectionUtils.isNotEmpty(tagIds)) {
            return tagMapper.selectBatchIds(tagIds);
        }
        return Collections.emptyList();
    }

    /**
     * 用户详情中的标签处理
     * 可能标签已经移除了，移除标签只需要更新库
     * 可能需要新增标签
     *
     * @param tags 用户身上的标签
     */
    @Override
    public void extContactTagsProcess(String extUserId, String userId, List<QyExternalUserTag> tags, Meta meta) {
        if (CollectionUtils.isEmpty(tags)) return;
        List<String> tagWxIds = tags.stream().map(QyExternalUserTag::getTag_id).collect(Collectors.toList());
        QueryWrapper<ContactTag> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "wx_id").eq("team_id", meta.getTeamId()).in("wx_id", tagWxIds);
        List<ContactTag> exitTags = tagMapper.selectList(queryWrapper);
        boolean retrieveTags = false;
        if (exitTags.size() < tagWxIds.size()) {
            log.info("标签数量不一致，开始同步不存在的企业微信标签数据");
            retrieveTags = true;
            List<String> exitWxIds = exitTags.stream().map(ContactTag::getWxId).collect(Collectors.toList());
            // 不存在的标签
            List<String> inexistTags = tags.stream()
                    .map(QyExternalUserTag::getTag_id)
                    .filter(tag_id -> !exitWxIds.contains(tag_id))
                    .collect(Collectors.toList());
            insertQywxTags(inexistTags, meta, this::insertTag);
        }
        // 绑定标签 > 员工 > 用户之间的关系
        if (retrieveTags) { // 重新获取更新后的数据
            exitTags = tagMapper.selectList(queryWrapper);
        }
        // 该用户有所需要绑定的标签id
        List<Long> extUserTagIds = exitTags.stream().map(ContactTag::getId).collect(Collectors.toList());
        // 已绑定的标签
        List<Long> isBindTagIds = extUserTagMapper.selectLongs(new QueryWrapper<ExtUserTag>()
                .select("tag_id")
                .eq("ext_user_id", extUserId)
                .eq("user_id", userId)
                .orderByAsc("tag_id"));
        List<ExtUserTag> addFollowerTag = exitTags.stream().filter(tag -> {
            int index = CollectionUtils.binarySearch(isBindTagIds, tag.getId());
            if (index >= 0) {
                isBindTagIds.remove(index);
                return false;
            }
            return true;
        }).map(t -> {
            ExtUserTag extUserTag = new ExtUserTag();
            extUserTag.setTagId(t.getId());
            extUserTag.setExtUserId(extUserId);
            extUserTag.setUserId(userId);
            return extUserTag;
        }).collect(Collectors.toList());

        InsertBatchFunction<ExtUserTag> insertBatchFunction = this::bindFollowerTag;
        insertBatchFunction.batch(100, addFollowerTag);
        // isBindTagIds 剩余的就是需要移除的标签
        this.unbindFollowerTag(extUserId, userId, isBindTagIds);
    }

    /**
     * 获取企业微信标签数据，并插入数据库
     */
    @Override
    public void insertQywxTags(List<String> tagWxIds, Meta meta, InsertBatchFunction<ContactTag> insertFun) {
        if (CollectionUtils.isEmpty(tagWxIds)) return;
        List<QyTagGroup> qyTagGroups = SyncAppApi.qywxTagsPost(meta, tagWxIds, null);
        if (CollectionUtils.isEmpty(qyTagGroups)) return;
//        List<String> groupNames = qyTagGroups.stream().map(QyTagGroup::getGroup_name).collect(Collectors.toList());
        List<String> groupWxIds = qyTagGroups.stream().map(QyTagGroup::getGroup_id).collect(Collectors.toList());
        // 存在的标签组
        List<ContactTagGroup> tagGroups = tagGroupMapper.selectList(new QueryWrapper<ContactTagGroup>()
                        .select("id", "group_name")
                        .eq("team_id", meta.getTeamId())
                        .in("wx_id", groupWxIds)
//                .in("group_name", groupNames)
        );
        List<ContactTag> contactTags = new ArrayList<>(tagWxIds.size());
        if (tagGroups.size() == groupWxIds.size()) { // 分组都存在
            Map<String, Long> reflects = tagGroups.stream().collect(Collectors.toMap(ContactTagGroup::getGroupName, ContactTagGroup::getId));
            for (QyTagGroup qyGroup : qyTagGroups) {
                qyGroup.getTag().stream().filter(t -> !t.isDeleted())
                        .forEach(t -> contactTags.add(ContactTag.builder()
                                .name(t.getName())
                                .wxId(t.getId())
                                .teamId(meta.getTeamId())
                                .type(TagEnum.QYWX.ordinal())
                                .groupId(reflects.get(qyGroup.getGroup_name()))
                                .build()));
            }
        } else {
            List<String> exitGroups = tagGroups.stream().map(ContactTagGroup::getGroupName).collect(Collectors.toList());
//            添加分组
            qyTagGroups.stream()
                    .filter(t -> !exitGroups.contains(t.getGroup_name()))
                    .forEach(t -> {
                        ContactTagGroup group = ContactTagGroup.builder()
                                .wxId(t.getGroup_id())
                                .groupName(t.getGroup_name())
                                .type(TagEnum.QYWX.ordinal())
                                .teamId(meta.getTeamId())
                                .build();
                        this.insertTagGroup(group);
                        // 添加分组下的标签
                        t.getTag().forEach(qyTag ->
                                contactTags.add(ContactTag.builder()
                                        .groupId(group.getId())
                                        .name(qyTag.getName())
                                        .teamId(meta.getTeamId())
                                        .type(TagEnum.QYWX.ordinal())
                                        .wxId(qyTag.getId())
                                        .build()));
                    });

        }
        // 入库
        insertFun.batch(100, contactTags);
    }

    @Transactional
    @Override
    public void updateQywxTags(List<String> tagWxIds, Meta meta) {
        if (CollectionUtils.isEmpty(tagWxIds)) return;
        List<QyTagGroup> qyTagGroups = SyncAppApi.qywxTagsPost(meta, tagWxIds, null);
        if (CollectionUtils.isEmpty(qyTagGroups)) return;
        qyTagGroups.forEach(group ->
                group.getTag().forEach(tag ->
                        tagMapper.update(null, new UpdateWrapper<ContactTag>()
                                .set("name", tag.getName())
                                .eq("wx_id", tag.getId())
                                .eq("team_id", meta.getTeamId())
                        )
                ));
    }

    @Transactional
    @Override
    public void updateQywxTagGroup(String groupWxId, Meta meta) {
        if (StrUtils.isEmpty(groupWxId)) return;
        List<QyTagGroup> qyTagGroups = SyncAppApi.qywxTagsPost(meta,
                null,
                Collections.singletonList(groupWxId));
        if (CollectionUtils.isEmpty(qyTagGroups)) return;
        qyTagGroups.forEach(group -> tagGroupMapper.update(null,
                new UpdateWrapper<ContactTagGroup>()
                        .set("group_name", group.getGroup_name())
                        .eq("team_id", meta.getTeamId())
                        .eq("wx_id", group.getGroup_id())
                )
        );
    }

    @Transactional
    @Override
    public void deleteTag(String tagWxId, Long teamId) {
        if (StrUtils.isEmpty(tagWxId) || !NumberUtils.isTrue(teamId)) {
            return;
        }
        QueryWrapper<ContactTag> query = new QueryWrapper<>();
        query.eq("wx_id", tagWxId).eq("team_id", teamId);
        ContactTag tag = tagMapper.selectOne(query);
        if (Objects.isNull(tag)) return;
        tagMapper.delete(query);
        Long surTagCount = tagOfGroupCount(tag.getGroupId());
        if (!NumberUtils.isTrue(surTagCount)) {
            tagGroupMapper.deleteById(tag.getGroupId());
        }
    }

    @Transactional
    @Override
    public void deleteTagGroup(String groupWxId, Long teamId) {
        if (StrUtils.isEmpty(groupWxId) || !NumberUtils.isTrue(teamId)) {
            return;
        }
        QueryWrapper<ContactTagGroup> query = new QueryWrapper<>();
        query.eq("wx_id", groupWxId).eq("team_id", teamId);
        ContactTagGroup group = tagGroupMapper.selectOne(query);
        if (Objects.isNull(group)) return;
        Long surTagCount = tagOfGroupCount(group.getId());
        tagGroupMapper.deleteById(group.getId());
        if (NumberUtils.isTrue(surTagCount)) {
            tagMapper.delete(new QueryWrapper<ContactTag>()
                    .eq("group_id", group.getId())
            );
        }
    }

    /**
     * 绑定标签
     */
    @Override
    public boolean extUserBindTags(String extUserId, String userId, List<Long> bindTags, Meta meta) {
        if (StrUtils.isEmpty(extUserId) || StrUtils.isEmpty(userId)) {
            log.error("绑定客户标签中用户id或员工id为空,extUserId:{},userId:{}", extUserId, userId);
            return false;
        }
        if (StrUtils.isNotEmpty(bindTags)) {
            // 所有已绑定的的标签
            List<Long> existTags = extUserTagMapper.selectLongs(new QueryWrapper<ExtUserTag>()
                    .select("tag_id")
                    .eq("ext_user_id", extUserId)
                    .eq("user_id", userId)
            );
            // 找到已经绑定的tag wxid
            List<ContactTag> tags = selectTagWxIdByIds(existTags);
            if (CollectionUtils.isEmpty(tags)) return true;
            // 已经绑定tag 转成MAP id = wxid
            Map<Long, String> exitTagMap = tags.stream()
                    .collect(Collectors.toMap(ContactTag::getId, ContactTag::getWxId));
            // 找到需要绑定的标签
            List<Long> needBindTagIds = bindTags.stream()
                    .filter(tagId -> !exitTagMap.containsKey(tagId))
                    .collect(Collectors.toList());

            List<String> bindTagWxIds = selectTagWxIdByIds(needBindTagIds)
                    .stream()
                    .map(ContactTag::getWxId)
                    .collect(Collectors.toList());
            // 找到需要解绑的标签
            List<Long> removeTagIds = existTags.stream()
                    .filter(tagId -> !bindTags.contains(tagId))
                    .collect(Collectors.toList());
            List<String> removeTagWxIds = removeTagIds.stream()
                    .map(exitTagMap::get)
                    .collect(Collectors.toList());
            // 同时为空 不进行任何操作
            if (CollectionUtils.isAllEmpty(bindTagWxIds, removeTagWxIds)) {
                return true;
            }
            // 请求接口 绑定标签
            boolean success = ContactTagApi.extUserMarkTags(extUserId, userId,
                    bindTagWxIds, removeTagWxIds,
                    meta);
            if (!success) return false;
            List<ExtUserTag> allBindTags = needBindTagIds.stream()
                    .map(bindId -> {
                        ExtUserTag extUserTag = new ExtUserTag();
                        extUserTag.setExtUserId(extUserId);
                        extUserTag.setUserId(userId);
                        extUserTag.setTagId(bindId);
                        return extUserTag;
                    }).collect(Collectors.toList());
            InsertBatchFunction<ExtUserTag> insertBatchFunction = this::bindFollowerTag;
            insertBatchFunction.batch(100, allBindTags);
            unbindFollowerTag(extUserId, userId, removeTagIds);
        }
        return true;
    }

    @Transactional
    public void bindFollowerTag(List<ExtUserTag> followerTags) {
        extUserTagMapper.insertBatchSomeColumn(followerTags);
    }

    @Transactional
    public void unbindFollowerTag(String extUserId, String userId, List<Long> followerTagIds) {
        if (CollectionUtils.isNotEmpty(followerTagIds)) {
            extUserTagMapper.delete(new QueryWrapper<ExtUserTag>()
                    .eq("ext_user_id", extUserId)
                    .eq("user_id", userId)
                    .in("tag_id", followerTagIds)
            );
        }
    }

    @Override
    public List<ExtUserTag> selectFollowerTags(List<String> extUserIds, List<String> userIds) {
        if (CollectionUtils.isNotEmpty(extUserIds) || CollectionUtils.isNotEmpty(userIds)) {
            return extUserTagMapper.selectFollowerTags(extUserIds, userIds);
        }
        return Collections.emptyList();
    }

    private Long tagOfGroupCount(Long groupId) {
        return tagMapper.selectCount(new QueryWrapper<ContactTag>().eq("group_id", groupId));
    }

    private List<ContactTag> selectTagWxIdByIds(List<Long> tagIds) {
        if (CollectionUtils.isEmpty(tagIds)) return Collections.emptyList();
        return tagMapper.selectList(
                new QueryWrapper<ContactTag>().select("id", "wx_id")
                        .in("id", tagIds)
                        .eq("type", 1)
        );
    }
}
